package com.tjvib.widget;

import android.app.Application;
import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.widget.Toast;

import com.tjvib.TJVIBAPP;
import com.tjvib.util.LogUtil;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 用途：全局异常拦截
 */
public class NeverCrash {

    private final static String TAG = NeverCrash.class.getSimpleName();
    private final static NeverCrash INSTANCE = new NeverCrash();
    private boolean debugMode;
    private static String PATH_LOGCAT;
    private MainCrashHandler mainCrashHandler;
    private UncaughtCrashHandler uncaughtCrashHandler;

    private NeverCrash() {
    }

    public static NeverCrash getInstance() {
        return INSTANCE;
    }


    private synchronized MainCrashHandler getMainCrashHandler() {
        if (null == mainCrashHandler) {
            mainCrashHandler = (t, e) -> {
            };
        }
        return mainCrashHandler;
    }

    /**
     * 主线程发生异常时的回调，可用于打印日志文件
     * <p>
     * 注意跨线程操作的可能
     */
    public NeverCrash setMainCrashHandler(MainCrashHandler mainCrashHandler) {
        this.mainCrashHandler = mainCrashHandler;
        return this;
    }

    private synchronized UncaughtCrashHandler getUncaughtCrashHandler() {
        if (null == uncaughtCrashHandler) {
            uncaughtCrashHandler = (t, e) -> {
            };
        }
        return uncaughtCrashHandler;
    }

    /**
     * 子线程发生异常时的回调，可用于打印日志文件
     * <p>
     * 注意跨线程操作的可能
     */
    public NeverCrash setUncaughtCrashHandler(UncaughtCrashHandler uncaughtCrashHandler) {
        this.uncaughtCrashHandler = uncaughtCrashHandler;
        return this;
    }

    private boolean isDebugMode() {
        return debugMode;
    }

    /**
     * debug模式，会打印log日志，且toast提醒发生异常，反之则都没有
     */
    public NeverCrash setDebugMode(boolean debugMode) {
        this.debugMode = debugMode;
        return this;
    }

    /**
     * 完成监听异常的注册
     * @param application application
     */
    public void register(Application application) {
        //主线程异常拦截
        new Handler(Looper.getMainLooper()).post(() -> {
            while (true) {
                try {
                    Looper.loop();
                } catch (Throwable e) {
                    if (isDebugMode()) {
                        LogUtil.e("未捕获的主线程异常行为", e);
                        new Handler(Looper.getMainLooper()).post(() ->
                                Toast.makeText(application, "主线程发生异常", Toast.LENGTH_LONG).show());
                    }
                    logExceptionToFile(e); // 记录异常到文件
                    getMainCrashHandler().mainException(Looper.getMainLooper().getThread(), e);
                }
            }
        });

        //子线程异常拦截
        Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
            if (isDebugMode()) {
                LogUtil.e("未捕获的子线程异常行为", e);
                new Handler(Looper.getMainLooper()).post(() ->
                        Toast.makeText(application, "子线程发生异常", Toast.LENGTH_LONG).show());
            }
            logExceptionToFile(e); // 记录异常到文件
            getUncaughtCrashHandler().uncaughtException(t, e);
        });
    }

    public interface MainCrashHandler {
        void mainException(Thread t, Throwable e);
    }

    public interface UncaughtCrashHandler {
        void uncaughtException(Thread t, Throwable e);
    }

    private void logExceptionToFile(Throwable e) {
        String timestamp = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(new Date());
        String fileName = "log-" + timestamp + ".log";
        File exportFolder = new File(TJVIBAPP.getApplication().getExternalFilesDir(null)+"/logs");
        if (!exportFolder.exists()) {
            if (!exportFolder.mkdir()) {
                LogUtil.e("创建文件夹失败");
                return;
            }
        }
        File logFile = new File(exportFolder, fileName);
        if (logFile.exists()) {
            logFile.delete();
        }

        try{
            if (!logFile.createNewFile()) {
                LogUtil.e("创建文件失败");
                return;
            }
            FileOutputStream fos = new FileOutputStream(logFile);
            OutputStreamWriter fileWriter = new OutputStreamWriter(fos);
            fileWriter.append("Timestamp: " + timestamp+"\n");
            fileWriter.append("Exception: " + e.toString()+"\n");
            fileWriter.append("StackTrace: "+"\n");
            for (StackTraceElement element : e.getStackTrace())
                fileWriter.append("\t" + element.toString()+"\n");
            fileWriter.append("\n");
            fileWriter.close();
            fos.close();
        } catch (IOException ioException) {
            LogUtil.e("Failed to write log file", ioException);
        }
    }

}